// 类型保护机制
enum Type { Strong, Week }
class Java {
  helloJava() {
    console.log('hello Java')
  }
  java: any
}

class JavaScript {
  helloJavaScript() {
    console.log('hello javascript')
  }
  javascript: any
}
function isJava(lang: JavaScript | Java): lang is Java {
  return (lang as Java).helloJava !== undefined
}

function getLanguage(type: Type, x: string | number) {
  let lang = type === Type.Strong ? new Java() : new JavaScript()

  if (isJava(lang)) {
    lang.helloJava()
  } else {
    lang.javascript()
  }
  // if ((lang as Java).helloJava) {
  //   (lang as Java).helloJava()
  // } else {
  //   (lang as JavaScript).helloJavaScript()
  // }

  // instanceof方法
  // if (lang instanceof Java) {
  //   lang.helloJava()
  // } else {
  //   lang.helloJavaScript()
  // }

  // in
  // if ('java' in lang) {
  //   lang.helloJava()
  // } else {
  //   lang.helloJavaScript()
  // }

  //typeof
  // if (typeof x === 'string') {
  //   x.length
  // } else {
  //   x.toFixed(2)
  // }
  return lang
}

// getLanguage(Type.Strong)

// 类型保护机制
//typescript 能够在特定的区块中保证变量属于某种确定的类型
//可以在此区块中放心地引用此类型地属性，或者调用此类型的方法
//4种方法
// instanceof
// in关键字
// typeof
// 创建一个保护函数


// 交叉类型
interface DogInterface {
  run(): void
}
interface CatInputface {
  jump(): void
}
let pet: DogInterface & CatInputface = {
  run() { },
  jump() { }
}


//联合类型
class Dog4 implements DogInterface {
  run() { }
  eat() { }
}
class Cat4 implements CatInputface {
  jump() { }
  eat() { }
}

enum Master { Boy, Girl }
function getPet(master: Master) {
  let pet = master === Master.Boy ? new Dog4() : new Cat4()
  pet.eat()
  return pet
}
//可区分的联合类型
interface Square {
  kind: 'square'
  size: number
}
interface Rectangle {
  kind: 'rectangle'
  width: number
  height: number
}
interface Circle {
  kind: 'circle'
  r: number
}
type Shape = Square | Rectangle | Circle
function area(s: Shape) {
  switch (s.kind) {
    case 'square':
      return s.size = s.size
    case 'rectangle':
      return s.height = s.width
    case 'circle':
      return Math.PI * s.r ** 2
    default:
      return ((e: never) => { throw new Error(e) })(s)
  }
}
console.log(area({ kind: 'circle', r: 1 }))


//索引类型
let obj4 = {
  a: 1,
  b: 2,
  c: 3
}
function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
  return keys.map(key => obj[key])
}
console.log(getValues(obj4, ['a', 'b']))
// console.log(getValues(obj4, ['e', 'f']))
// 索引类型的查询操作符 keyof T
interface obj5 {
  a: number,
  b: string
}
let key: keyof obj5
let value: obj5['a']


//映射类型
interface Obj6 {
  a: string
  b: number
  c: boolean
}
type ReadonlyObj = Readonly<Obj6>
type PartialObj = Partial<Obj6>
type PickObj = Pick<Obj6, 'a' | 'b'>
type RecordObj = Record<'x' | 'y', Obj6>

//条件类型
type TypeName<T> =
  T extends string ? 'string' :
  T extends number ? 'number' :
  T extends boolean ? 'boolean' :
  T extends undefined ? 'undefined' :
  T extends Function ? 'function' :
  'object';

type T1 = TypeName<string>
type T2 = TypeName<string[]>

// 分布式类型
type T3 = TypeName<string | string[]>
type Diff<T, U> = T extends U ? never : T
type T4 = Diff<'a' | 'b' | 'c', "a" | 'e'>
type NotNull<T> = Diff<T, undefined | null>
type T5 = NotNull<string | number | undefined | null>
//exclude<T,U> // 剔除
//NotNull<T>
//Extract<T,U> //抽取

type T6 = Extract<'a' | 'b' | 'c', 'a' | 'e'>

// ReturnType<T> 获取函数返回值
type T7 = ReturnType<() => string>